package com.hk.commons.util;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.hk.commons.annotations.HandlerType;
import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.springframework.core.annotation.AnnotationUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;


/**
 * @author Kevin
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class HandlerTypeFactory {

    private static final ConcurrentHashMap<String, List<HandlerModel>> MAP = new ConcurrentHashMap<>();

    @Data
    @Accessors(chain = true)
    public static class HandlerModel {

        private String beanName;

        @JsonIgnore
        private String[] value;

        private String description;

        @JsonIgnore
        private Object bean;
    }

    public static <T> Optional<T> getFirstBeanOfValue(Class<T> clazz, String value) {
        return CollectionUtils.getFirstOrDefault(getBeanOfValue(clazz, value));
    }

    public static <T> List<T> getBeanOfValue(Class<T> clazz, String value) {
        return getByType(clazz).stream()
                .filter(item -> ArrayUtils.contains(item.getValue(), value))
                .map(item -> clazz.cast(item.getBean()))
                .collect(Collectors.toList());
    }

    public static <T> List<HandlerModel> getByType(Class<T> clazz) {
        var beanName = clazz.getSimpleName();
        var valueList = MAP.get(beanName);
        if (Objects.isNull(valueList)) {
            var beanMap = SpringContextHolder.getBeanOfType(clazz);
            var modelList = new ArrayList<HandlerModel>();
            beanMap.forEach((key, value) -> {
                var handlerType = AnnotationUtils.findAnnotation(value.getClass(), HandlerType.class);
                if (null != handlerType) {
                    modelList.add(new HandlerModel()
                            .setBeanName(key)
                            .setValue(handlerType.value())
                            .setDescription(handlerType.description())
                            .setBean(value));
                }
            });
            MAP.put(beanName, modelList);
            valueList = modelList;
        }
        return valueList;
    }
}
